#ifndef FETMAT_NET_SOCKET_STREAM_H_
#define FETMAT_NET_SOCKET_STREAM_H_
#include <fermat/common/buffer_slice.h>
#include <fermat/common/types.h>
#include <sys/uio.h>
#include <cerrno>

namespace fermat{
namespace net {
template < size_t N>
class SocketStream{
public:
	SocketStream(fermat::PageBufferSliceQueuePtr ptr)
	{
		assert(ptr);
		_ptr = ptr;
		_size = 0;
		PageBufferSliceQueueItr it = _ptr->begin();
		while(it != _ptr->end()) {
			PageBufferSlicePtr bufferptr(*it);
			_size += bufferptr->size();
			it++;
		}
	}

	ssize_t readFromFd(int fd, int &saved_error)
	{
		PageBufferSlicePtr buffer_array[N];
		for(size_t i = 0; i < N; i++) {
			buffer_array[i].reset(new PageBufferSlice());
			_vec[i].iov_base = buffer_array[i]->writePos();
			_vec[i].iov_len  = buffer_array[i]->writeAbleSize();
		}
		ssize_t n = ::readv(fd, _vec, N);
		if(n < 0) {
			saved_error = errno;
		} else {
			size_t read_size = implicit_cast<size_t>(n);
			_size += read_size;
			for(size_t i = 0; i < N; i++) {
				if(read_size ==0) {
					break;
				}
				if(buffer_array[i]->capacity() >= read_size) {
					buffer_array[i]->writed(buffer_array[i]->capacity());
					read_size -= buffer_array[i]->capacity();
				} else {
					buffer_array[i]->writed(read_size);
					read_size = 0;
				}
				_ptr->push_back(buffer_array[i]);
			}
		}
		return n;
	}

	ssize_t writeToFd(int fd, size_t limited , int &saved_error)
	{
		PageBufferSliceQueueItr it = _ptr->begin();
		size_t count = 0;
		size_t total_send_limit = limited == 0 ? _size : limited;
		while(count < N -1 && total_send_limit > 0 && it != _ptr->end()) {
			PageBufferSlicePtr bufferptr(*it);
			if(bufferptr->size() == 0){
				continue;
			}
			_vec[count].iov_base = bufferptr->mutablePeek();
			if(bufferptr->size() <= total_send_limit){
				_vec[count].iov_len = bufferptr->size();
				total_send_limit -= bufferptr->size();
			} else {
				_vec[count].iov_len = total_send_limit;
				total_send_limit = 0;	
			}
			count++;
			it++;
		}

		ssize_t n = ::writev(fd, _vec, count);
		if(n < 0) {
			saved_error = errno;
		} else {
			size_t sended = implicit_cast<size_t>(n);
			_size -= sended;
			while(sended > 0) {
				PageBufferSlicePtr fptr = _ptr->front();
				if(sended > fptr->size()) {
					sended -= fptr->size();
					_ptr->pop_front();
				} else {
					fptr->retrieve(sended);
					sended = 0;
				}	
			}
		}
		return n;

	}

	size_t size()
	{
		return _size;
	}


private:
	PageBufferSliceQueuePtr _ptr;
	size_t                  _size;
	struct iovec            _vec[N];

};
}
}
#endif